ExpandableStatement.java
package org.codefilarete.stalactite.sql.statement;
import java.sql.PreparedStatement;
import java.util.Map;
import java.util.function.BiConsumer;
import org.codefilarete.stalactite.sql.statement.binder.PreparedStatementWriter;
import org.codefilarete.stalactite.sql.statement.binder.PreparedStatementWriterIndex;
/**
* Equivalent to {@link PreparedSQL} but with ParamType that can be expanded (essentially for Collection parameters)
*
* @author Guillaume Mary
*/
public abstract class ExpandableStatement<ParamType> extends SQLStatement<ParamType> {
private final String sql;
public ExpandableStatement(String sql, Map<? extends ParamType, ? extends PreparedStatementWriter<?>> parameterBinders) {
super(parameterBinders);
this.sql = sql;
}
public ExpandableStatement(String sql, PreparedStatementWriterIndex<? extends ParamType, ? extends PreparedStatementWriter<?>> parameterBinderProvider) {
super(parameterBinderProvider);
this.sql = sql;
}
@Override
public String getSQL() {
return sql;
}
@Override
protected void doApplyValue(ParamType paramType, Object value, PreparedStatement statement) {
PreparedStatementWriter<Object> parameterBinder = getParameterBinder(paramType);
if (parameterBinder == null) {
throw new BindingException("Can't find binder for parameter \"" + getParameterName(paramType) + "\""
+ " of type " + (value == null ? "unknown" : value.getClass().getName())
+ " (value = " + value + ")"
+ " on sql : " + getSQL());
}
adaptIterablePlaceholders(value, getIndexes(paramType), (v, markIndex) -> doApplyValue(v, markIndex, parameterBinder, statement));
}
public static void adaptIterablePlaceholders(Object value, int[] markIndexes, BiConsumer<Integer, Object> placeholderIndexValueConsumer) {
if (markIndexes.length > 1 && value instanceof Iterable) {
// we have several mark indexes : one per value, and one per parameter in query ("id = :id or id = :id)
// so we loop twice
for (int i = 0; i < markIndexes.length;) {
for (Object v : (Iterable) value) {
int markIndex = markIndexes[i];
placeholderIndexValueConsumer.accept(markIndex, v);
i++;
}
}
} else {
// cases:
// - simple case: one mark index and a single value, we loop on indexes
// - multiple mark indexes and a single value => all indexes will have the same value
// - one mark index and multiple values (List or any Iterable in fact) => binder should handle this case (ComplexTypeBinder for instance)
for (int markIndex : markIndexes) {
placeholderIndexValueConsumer.accept(markIndex, value);
}
}
}
protected abstract String getParameterName(ParamType parameter);
protected abstract int[] getIndexes(ParamType paramType);
}